package com.google.sydym6.logic.managers.recognizing;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashSet;
import java.util.Set;

import javazoom.jl.decoder.Bitstream;
import javazoom.jl.decoder.Decoder;
import javazoom.jl.decoder.Header;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.decoder.OutputChannels;

import android.content.Context;

import com.google.sydym6.logic.jlayer.Converter;
import com.google.sydym6.logic.observers.IRecognizeStatusObservable;
import com.google.sydym6.logic.observers.IRecognizeStatusObserver;
import com.gracenote.mmid.MobileSDK.GNConfig;
import com.gracenote.mmid.MobileSDK.GNOperationStatusChanged;
import com.gracenote.mmid.MobileSDK.GNOperations;
import com.gracenote.mmid.MobileSDK.GNSampleBuffer;
import com.gracenote.mmid.MobileSDK.GNSearchResult;
import com.gracenote.mmid.MobileSDK.GNSearchResultReady;
import com.gracenote.mmid.MobileSDK.GNStatus;

public class RecognizeAgent implements IRecognizeAgent,GNSearchResultReady, GNOperationStatusChanged, IRecognizeStatusObservable {
	GNConfig config;
	GNSearchResult result = null;
	Object resultSynchronizeObject;
	
	String recognizeStatus;
	
	Set<IRecognizeStatusObserver> recognizeStatusObservers;
	
	public RecognizeAgent(Context context) {
		config = GNConfig.init("5435392-85C21DCCC8BBE15A8B5EE2BDC8A9ACDC", context);
		resultSynchronizeObject = new Object();

		recognizeStatusObservers = new HashSet<IRecognizeStatusObserver>();
	}

	@Override
	public GNSearchResult recognize(String urlString) throws JavaLayerException, IOException, InterruptedException {
		System.out.println("RecognizeAgent Thread.currentThread().getId(): " + Thread.currentThread().getId());
		System.out.println("RecognizeAgent Thread.currentThread().isInterrupted(): " + Thread.currentThread().isInterrupted());
		
		InputStream input = null;
		
		URL urlConn = new URL(urlString);
		input = urlConn.openStream(); 
       
		byte[] pcmBuffer = null;
		Bitstream stream = new Bitstream(input);
		Header header = null;

		recognizeStatus = "Converting";
		notifyRecognizeStatusObservers();
		
		header = stream.readFrame();
       
		int channel = header.mode() == 3 ? 1 : 2;
		int sampleRate = header.frequency();
		System.out.println("channel: " + channel + " sampleRate: " + sampleRate);
		int length = (int) (channel * sampleRate * 2 * 6.5);
		System.out.println("int length = " + length);
		 
		Decoder.Params dp = new Decoder.Params();
		dp.setOutputChannels(OutputChannels.BOTH);
		Converter converter = new Converter();
		short[] shortBuffer = converter.convert(input, dp, length / 2);
		System.out.println("Converted" + length);
		pcmBuffer = new byte[shortBuffer.length * 2];
		ByteBuffer.wrap(pcmBuffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shortBuffer);
		System.out.println("Converted to byte pcmBuffer length = " + pcmBuffer.length);
		stream.close();
		input.close();
		
		if(!Thread.currentThread().isInterrupted()) {
			GNSampleBuffer gnSampleBuffer = new GNSampleBuffer(pcmBuffer, channel, sampleRate);
			GNOperations.recognizeMIDStreamFromPcm(this, config, gnSampleBuffer);
			synchronized(resultSynchronizeObject) {
				resultSynchronizeObject.wait();
			}
		}
			
		return result;
	}

	@Override
	public void GNResultReady(GNSearchResult result) {
		synchronized(resultSynchronizeObject) {
			this.result = result; 
			resultSynchronizeObject.notifyAll();
		}
	}

	@Override
	public void GNStatusChanged(GNStatus status) {
		recognizeStatus = status.getMessage();
		notifyRecognizeStatusObservers();
	}

	@Override
	public String getRecognizeStatus() {
		return recognizeStatus;
	}
	
	@Override
	public void addObserver(IRecognizeStatusObserver o) {
		recognizeStatusObservers.add(o);
	}

	@Override
	public void removeObserver(IRecognizeStatusObserver o) {
		recognizeStatusObservers.remove(o);
	}

	@Override
	public void notifyRecognizeStatusObservers() {
		for(IRecognizeStatusObserver o : recognizeStatusObservers)
			o.updateRecognizeStatus();
	}
	
}
